package com.aididi;

import com.aididi.utils.RedisLock;
import com.aididi.utils.RedisLockUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * redis分布式锁
 */
@SpringBootApplication
@Slf4j
public class RedisDistributedApplication {

	@Autowired
	private static RedisLock redisLock;
	@Autowired
	private static RedisLockUtil redisLockUtil;

	public static void main(String[] args) throws InterruptedException {
		SpringApplication.run(RedisDistributedApplication.class, args);


		//redis key
		String lockKey = "lock:test";

		CountDownLatch start = new CountDownLatch(1);
		CountDownLatch threadsLatch = new CountDownLatch(2);
		//锁的自动释放时间(秒)
		final int lockExpireSecond = 5;
		//未获取到锁的请求，尝试重试的最久等待时间(秒)
		final int timeoutSecond = 3;
		Runnable runnable = () -> {
			String lockValue = "";
			try {
				//等待发令枪响，防止线程抢跑
				start.await();

				//允许丢数据的简单锁示例
				lockValue = redisLock.simpleLock(lockKey, lockExpireSecond);

				//不允许丢数据的分布式锁示例
				//lockValue = redisLock.lock(lockKey, lockExpireSecond, timeoutSecond);

				//停一会儿，故意让后面的线程抢不到锁
				TimeUnit.SECONDS.sleep(2);
				log.info(String.format("%s get lock successfully, value:%s", Thread.currentThread().getName(), lockValue));

			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				redisLock.unlock(lockKey, lockValue);
				//执行完后，计数减1
				threadsLatch.countDown();
			}
        };

		//启动两个线程模拟并发场景

		/**
		 * 1. 简单锁示例，可以看到T2线程没抢到锁，直接抛出了预期的异常。
		 * 2.分布式锁示例，可以看到，T1先抢到锁，然后经过2秒的处理后，锁释放，这时T2重试拿到了锁，继续处理，最终释放。
		 */
		Thread t1 = new Thread(runnable, "T1");
		Thread t2 = new Thread(runnable, "T2");

		t1.start();
		t2.start();

		//预备：开始！
		start.countDown();

		//等待所有线程跑完
		threadsLatch.await();

		log.info("======>done!!!");

	}

	/**
	 * 下单扣库存
	 * @param orderId
	 * @return
	 */
	@PostMapping("order/{orderId}")
	public Object order(@PathVariable("orderId") String orderId){
		//redis key
		String lockKey = "lock:test";
		//获取锁
		try {
			Boolean lock = redisLockUtil.tryLock(lockKey, orderId, 100);
			if (null != lock && lock) {
					//TODO 业务处理

			} else {
				log.warn("redis锁获取失败|{}|{}", lockKey, lock);
			}
		}catch (Exception e){
			e.printStackTrace();
		}finally {
			//释放锁
			Boolean release = redisLockUtil.releaseLock(lockKey, orderId);
		}
		return null;
	}

}
